package cn.t.service.user;

import cn.t.core.exception.BizException;
import cn.t.core.mode.YesNoEnum;
import cn.t.core.service.ServiceImpl;
import cn.t.dto.user.CustomerFirmDTO;
import cn.t.core.enums.base.BaseStatusEnum;
import cn.t.enums.user.PassportConstant;
import cn.t.enums.user.PassportTypeEnum;
import cn.t.enums.user.UserError;
import cn.t.model.user.UserPassportDO;
import cn.t.model.user.UserQueryDO;
import cn.t.repository.user.UserPassportRepository;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 通行证表领域服务
 *
 * @author l
 */
@Slf4j
@Service
@AllArgsConstructor
public class UserPassportService extends ServiceImpl<UserPassportDO, UserPassportRepository> {

    public Long add(Long userId, UserPassportDO userPassport) {
        if (userId == null || userId < 1L || userPassport == null) {
            return null;
        }
        userPassport.setUserId(userId);
        userPassport.setPassportStatus(String.valueOf(BaseStatusEnum.AVAILABLE.getCode()));
        userPassport.setIsDeleted(YesNoEnum.FALSE);

        return addAndReturnId(userPassport);
    }

    public Long update(Long userId, UserPassportDO userPassport) {
        if (userId == null || userId < 1L || userPassport == null) {
            return null;
        }

        UserPassportDO existedPassport = findByPassportType(userId, userPassport.getPassportType());
        if (existedPassport != null) {
            existedPassport.setPassportValue(userPassport.getPassportValue());
            update(existedPassport);
            return existedPassport.getId();
        } else {
            return add(userId, userPassport);
        }
    }

    public UserPassportDO checkMessageCode(String phoneNumber, Integer messageCode) {

        String redisKey = String.format(PassportConstant.REDIS_KEY__PASSPORT_PHONE_CODE, phoneNumber);
        Integer code = (Integer) redissonClient.getBucket(redisKey).get();
        if (code == null || !code.equals(messageCode)) {
            log.warn("验证码验证失败，手机号：{} 验证码：{} - {}", phoneNumber, code, messageCode);
            throw new BizException(UserError.VALIDATE_CODE_FAILED, UserError.VALIDATE_CODE_FAILED.getName());
        }

        UserPassportDO passport = getUserPassportByPhoneNumber(phoneNumber);
        if (passport == null) {
            log.warn("手机号不存在或不可用，手机号：{}", phoneNumber);
            throw new BizException(UserError.PASSPORT_IS_NOT_EXISTS, UserError.PASSPORT_IS_NOT_EXISTS.getName());
        }

        if (!String.valueOf(BaseStatusEnum.AVAILABLE.getCode()).equals(passport.getPassportStatus())) {
            throw new BizException(UserError.PASSPORT_IS_NOT_AVAILABLE, UserError.PASSPORT_IS_NOT_AVAILABLE.getName());
        }

        return passport;
    }

    public Integer generateMessageCode(String phoneNumber) {
        String redisKey = String.format(PassportConstant.REDIS_KEY__PASSPORT_PHONE_CODE, phoneNumber);
        Integer code = (Integer) redissonClient.getBucket(redisKey).get();
        if (code != null) {
            log.warn("验证码刷新过快，手机号：{}", phoneNumber);
            throw new BizException(UserError.GENERATE_VALIDATE_CODE_TOO_FAST, UserError.GENERATE_VALIDATE_CODE_TOO_FAST.getName());
        }

        UserPassportDO passport = getUserPassportByPhoneNumber(phoneNumber);
        if (passport == null) {
            throw new BizException(UserError.PASSPORT_IS_NOT_EXISTS, UserError.PASSPORT_IS_NOT_EXISTS.getName());
        }
        if (!String.valueOf(BaseStatusEnum.AVAILABLE.getCode()).equals(passport.getPassportStatus())) {
            throw new BizException(UserError.PASSPORT_IS_NOT_AVAILABLE, UserError.PASSPORT_IS_NOT_AVAILABLE.getName());
        }

        code = ThreadLocalRandom.current().nextInt(100000, 1000000);

        redissonClient.getBucket(redisKey).set(code, PassportConstant.REDIS_KEY_PASSPORT_PHONE_CODE_TIMEOUT, TimeUnit.MINUTES);

        return code;
    }

    public void checkPassword(UserPassportDO passport, String password) {
        if (passport == null) {
            log.warn("用户不存在 ");
            throw new BizException(UserError.PASSPORT_IS_NOT_EXISTS, UserError.PASSPORT_IS_NOT_EXISTS.getName());
        }

        userInfoService.checkPassword(passport.getUserId(), password);
    }

    public UserPassportDO checkPassword(String userName, String password) {
        UserPassportDO passport = getUserPassportByUserName(userName);
        if (passport == null) {
            log.warn("用户名不存在 用户名：{}", userName);
            throw new BizException(UserError.PASSPORT_IS_NOT_EXISTS, UserError.PASSPORT_IS_NOT_EXISTS.getName());
        }

        userInfoService.checkPassword(passport.getUserId(), password);
        return passport;
    }

    public UserPassportDO getUserPassportByPhoneNumber(String phoneNumber) {
        UserPassportDO template = new UserPassportDO();
        template.setPassportType(PassportTypeEnum.PHONE_NUMBER.getCode());
        template.setPassportValue(phoneNumber);
        template.setIsDeleted(YesNoEnum.FALSE);

        List<UserPassportDO> results = repository.findByModel(template, 0, 10, "gmt_created", "asc");
        if (results == null || results.isEmpty()) {
            return null;
        }

        if (results.size() > 1) {
            log.warn("存在多个通行证，手机号：{}", phoneNumber);
        }
        return results.get(0);
    }

    public UserPassportDO getUserPassportByUserName(String userName) {
        UserPassportDO template = new UserPassportDO();
        template.setPassportType(PassportTypeEnum.USER_NAME.getCode());
        template.setPassportValue(userName);
        template.setPassportStatus(String.valueOf(BaseStatusEnum.AVAILABLE.getCode()));
        template.setIsDeleted(YesNoEnum.FALSE);

        List<UserPassportDO> results = repository.findByModel(template, 0, 10, "gmt_created", "asc");
        if (results == null || results.isEmpty()) {
            return null;
        }

        if (results.size() > 1) {
            log.warn("存在多个通行证，用户名：{}", userName);
        }
        return results.get(0);
    }

    public UserPassportDO getUserPassportByEmployeeCode(Long firmId, String employeeCode) {
        List<UserPassportDO> userPassportList = getUserPassportByTypeValue(firmId, PassportTypeEnum.EMP_CODE.getCode(), employeeCode);
        if (!CollectionUtils.isEmpty(userPassportList)) {
            return userPassportList.get(0);
        }

        String errorMessage = String.format("存在重复的通行证. 企业ID=%s 员工号=%s", firmId, employeeCode);
        log.warn(errorMessage);
        throw new BizException(UserError.DUPLICATED_PASSPORT, UserError.DUPLICATED_PASSPORT.getName());
    }

    public UserPassportDO getUserPassportByCardCode(Long firmId, String cardCode) {
        List<UserPassportDO> userPassportList = getUserPassportByTypeValue(firmId, PassportTypeEnum.CARD_CODE.getCode(), cardCode);
        if (!CollectionUtils.isEmpty(userPassportList)) {
            return userPassportList.get(0);
        }

        String errorMessage = String.format("存在重复的通行证. 企业ID=%s 卡号=%s", firmId, cardCode);
        log.warn(errorMessage);
        throw new BizException(UserError.DUPLICATED_PASSPORT, UserError.DUPLICATED_PASSPORT.getName());
    }


    public List<UserPassportDO> getUserPassportByTypeValue(Long firmId, Integer passportType, String passportValue) {

        UserPassportDO template = new UserPassportDO();
        template.setPassportType(passportType);
        template.setPassportValue(passportValue);
        template.setIsDeleted(YesNoEnum.FALSE);

        if (PassportTypeEnum.CARD_CODE.getCode().equals(passportType)) {
            List<UserQueryDO> userQueryList = userQueryService.findByCardCode(firmId, passportValue);
            template.setUserId(getAvailableUserId(userQueryList, firmId, passportValue));
        } else if (PassportTypeEnum.EMP_CODE.getCode().equals(passportType)) {
            List<UserQueryDO> userQueryList = userQueryService.findByEmployeeCode(firmId, passportValue);
            template.setUserId(getAvailableUserId(userQueryList, firmId, passportValue));
        }


        return repository.findByModel(template, 0, 100, "gmt_created", "asc");
    }

    private Long getAvailableUserId(List<UserQueryDO> userQueryList, Long firmId, String passportValue) {
        if (CollectionUtils.isEmpty(userQueryList)) {
            String errorMessage = String.format("指定的通行证不可用 passportType=%s passportValue=%s", PassportTypeEnum.EMP_CODE, passportValue);
            log.error(errorMessage);
            throw new BizException(UserError.PASSPORT_IS_NOT_EXISTS, UserError.PASSPORT_IS_NOT_EXISTS.getName());
        }

        UserQueryDO userQueryDO = null;
        for (UserQueryDO tmp : userQueryList) {
            if (tmp != null && YesNoEnum.FALSE.equals(tmp.getIsDeleted()) && String.valueOf(BaseStatusEnum.AVAILABLE.getCode()).equals(tmp.getUserStatus())) {
                if (userQueryDO != null) {
                    String errorMessage = String.format("存在重复的通行证. 企业ID=%s 卡号=%s", firmId, passportValue);
                    log.warn(errorMessage);
                    throw new BizException(UserError.DUPLICATED_PASSPORT, UserError.DUPLICATED_PASSPORT.getName());
                }
                userQueryDO = tmp;
            }
        }
        if (userQueryDO == null) {
            throw new BizException(UserError.PASSPORT_IS_NOT_AVAILABLE, UserError.PASSPORT_IS_NOT_AVAILABLE.getName());
        }

        return userQueryDO.getId();


    }


    public UserPassportDO findByPassportType(Long userId, Integer passportType) {
        if (userId == null || passportType == null || PassportTypeEnum.getByCode(passportType).getCode() == null) {
            return null;
        }

        UserPassportDO template = new UserPassportDO();
        template.setPassportStatus(String.valueOf(BaseStatusEnum.AVAILABLE.getCode()));
        template.setIsDeleted(YesNoEnum.FALSE);
        template.setPassportType(passportType);
        template.setUserId(userId);

        List<UserPassportDO> results = repository.findByModel(template, 0, 10, "gmt_created", "asc");
        if (results == null || results.isEmpty()) {
            return null;
        }
        if (results.size() > 1) {
            log.warn("存在多个通行证，userId={} passportType={}", userId, passportType);
        }

        return results.get(0);
    }

    public List<UserPassportDO> findByUserIdIgnoreDisable(Long userId) {
        if (userId == null) {
            return null;
        }
        UserPassportDO template = new UserPassportDO();
        template.setIsDeleted(YesNoEnum.FALSE);
        template.setUserId(userId);
        return repository.findByModel(template, 0, 10, "gmt_created", "desc");
    }

    public List<UserPassportDO> findByUserId(Long userId) {
        if (userId == null) {
            return null;
        }

        UserPassportDO template = new UserPassportDO();
        template.setPassportStatus(String.valueOf(BaseStatusEnum.AVAILABLE.getCode()));
        template.setIsDeleted(YesNoEnum.FALSE);
        template.setUserId(userId);
        return repository.findByModel(template, 0, 10, "gmt_created", "asc");
    }

    public void deletePassportByUserId(Long userId) {
        List<UserPassportDO> passportList = findByUserId(userId);
        if (passportList == null || passportList.isEmpty()) {
            return;
        }
        passportList.forEach(passportDO -> repository.deletePassportById(passportDO.getId()));
    }


    public void hasDuplicatedPassport(Long userId, CustomerFirmDTO customerFirmDTO, UserPassportDO userPassportDO) {
        if (userPassportDO == null || userPassportDO.getPassportType() == null || userPassportDO.getPassportValue() == null) {
            String errorMessage = String.format("指定的通行证不可用 userId=%d userPassportDO=%s", userId, userPassportDO);
            log.error(errorMessage);
            throw new BizException(UserError.PASSPORT_IS_NOT_AVAILABLE, errorMessage);
        }

        // 员工号 及 卡号 在当前企业下的唯一性校验
        if (PassportTypeEnum.EMP_CODE.getCode().equals(userPassportDO.getPassportType()) || PassportTypeEnum.CARD_CODE.getCode().equals(userPassportDO.getPassportType())) {
            if (customerFirmDTO == null || customerFirmDTO.getId() == null) {
                return;
            }
            List<UserQueryDO> userQueryList = null;
            if (PassportTypeEnum.EMP_CODE.getCode().equals(userPassportDO.getPassportType())) {
                userQueryList = userQueryService.findByEmployeeCode(customerFirmDTO.getId(), userPassportDO.getPassportValue());
            } else {
                userQueryList = userQueryService.findByCardCode(customerFirmDTO.getId(), userPassportDO.getPassportValue());
            }

            if (CollectionUtils.isEmpty(userQueryList)) {
                return;
            }

            Set<Long> userIds = userQueryList.stream().map(UserQueryDO::getId).collect(Collectors.toSet());
            if (userId == null || !userIds.contains(userId)) {
                String errorMessage = String.format("存在重复的通行证 userId=%s passportType=%s passportValue=%s", userId, PassportTypeEnum.getByCode(userPassportDO.getPassportType()).getName(), userPassportDO.getPassportValue());
                log.error(errorMessage);
                throw new BizException(UserError.DUPLICATED_PASSPORT, errorMessage);
            }

        } else {
            // 手机号、用户名、企业站 ID 唯一性校验
            String passportValue = userPassportDO.getPassportValue();
            PassportTypeEnum newPassportType = PassportTypeEnum.getByCode(userPassportDO.getPassportType());
            UserPassportDO template = getUserPassportDO(newPassportType, passportValue);
            List<UserPassportDO> tmp = repository.findByModel(template, 0, 10000, "user_id", "asc");
            if (tmp == null || tmp.isEmpty()) {
                return;
            }

            for (UserPassportDO existsPassport : tmp) {
                if (userId == null || !userId.equals(existsPassport.getUserId())) {
                    String errorMessage = String.format("存在重复的通行证 userId=%s passportType=%s passportValue=%s", userId, newPassportType.getName(), passportValue);
                    log.error(errorMessage);
                    throw new BizException(UserError.DUPLICATED_PASSPORT, errorMessage);
                }
            }
        }

    }

    private UserPassportDO getUserPassportDO(PassportTypeEnum newPassportType, String passportValue) {
        UserPassportDO template = new UserPassportDO();

        template.setPassportType(newPassportType.getCode());
        template.setPassportValue(passportValue);
        template.setPassportStatus(String.valueOf(BaseStatusEnum.AVAILABLE.getCode()));
        template.setIsDeleted(YesNoEnum.FALSE);
        return template;
    }

    public void removeWithout(Long userId, Set<Long> passportIds) {

        if (CollectionUtils.isEmpty(passportIds)) {
            return;
        }

        UserPassportDO template = new UserPassportDO();
        template.setPassportStatus(String.valueOf(BaseStatusEnum.AVAILABLE.getCode()));
        template.setIsDeleted(YesNoEnum.FALSE);
        template.setUserId(userId);

        List<UserPassportDO> tmp = repository.findByModel(template, 0, 1000, "user_id", "asc");
        if (tmp == null || tmp.isEmpty()) {
            return;
        }

        for (UserPassportDO exists : tmp) {
            if (!passportIds.contains(exists.getId())) {
                removeById(exists.getId());
            }
        }

    }


    public void disablePassportByUserId(Long userId) {
        repository.disablePassportByUserId(userId);
    }

    public void enablePassportByUserId(Long userId) {
        repository.enablePassportByUserId(userId);
    }

    @Autowired
    private RedissonClient redissonClient;


    @Autowired
    private UserInfoService userInfoService;

    @Autowired
    private UserQueryService userQueryService;


}
